﻿/////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2009 Eric Bowen
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 
// associated documentation files (the "Software"), to deal in the Software without restriction, including 
// without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 
// copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the 
// following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial 
// portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
/////////////////////////////////////////////////////////////////////////////////////////////////////
// Author's blog: http://blog.scrappydog.com
// Updated source code available:  http://scrappydb.codeplex.com
/////////////////////////////////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
using Amazon.SimpleDB.Model;
using ScrappyDB.BaseClasses;
using ScrappyDB.Utilities;

namespace ScrappyDB
{
    public partial class Db
    {
        //TODO: Cascading deletes

        public void Delete<T>(T entity) where T : class, new()
        {
            DeleteInternal<T>(EntityHelper.GetEncodedItemNameValue(entity));
        }

        public void Delete<T>(SdbCollection<T> entities) where T : class, new()
        {
            DeleteIEnumerable(entities);
        }

        public void Delete<T>(IEnumerable<T> entities) where T : class, new()
        {
            DeleteIEnumerable(entities);
        }

        private void DeleteIEnumerable<T>(IEnumerable<T> entities) where T : class, new()
        {
            if (entities == null)
                return;

            var list = new List<string>();

            int count = 0;
            foreach (var entity in entities)
            {

                list.Add(Utilities.EntityHelper.GetEncodedItemNameValue(entity));
                count++;
            }
            DeleteInternal<T>(list);
            var o = new T();
            string domainName = EntityHelper.GetDomainName(o);
            Log.Info("ScrappyDB.Delete<T>(ScrappyDbCollection<T>): Domain: {0} - Entity Type: {1} - Record Count: {2} ", domainName, EntityHelper.GetEntityName(o), count);
        }

        public void Delete<T>(params object[] ids) where T : class, new()
        {
            var list = new List<string>();
            foreach (var id in ids)
            {
                string encodedId = EntityHelper.EncodeItemNameValue<T>(id);
                list.Add(encodedId);
            }

            DeleteInternal<T>(list);
        }

        private void DeleteInternal<T>(string encodedItemNameValue) where T : class, new()
        {
            Log.Debug("DB.Delete Start: " + DateTime.Now.ToLongTimeString());

            var o = new T();
            string domainName = EntityHelper.GetDomainName(o);
            var request = new DeleteAttributesRequest { DomainName = domainName, ItemName = encodedItemNameValue };

            try
            {
                DeleteAttributesResponse response = Service.DeleteAttributes(request);

                Log.Info(
                    "ScrappyDB.Delete<T>: Domain: {0} - Entity: {1} - Id: {2} ",
                    domainName, EntityHelper.GetEntityName(o), encodedItemNameValue);

                //clear cache
                if (WebCache != null)
                {
                    WebCache.Remove(EntityCacheKey(domainName, encodedItemNameValue));
                    Log.Debug("CACHE DELETE: key - {0}", EntityCacheKey(domainName, encodedItemNameValue));
                }

                if (response.IsSetResponseMetadata())
                    Utility.TraceResponse(response.ResponseMetadata, response.ToXML());
            }
            catch (Exception ex)
            {
                if (ex.Message.Contains("does not have permission"))
                    throw new SdbExceptions.SdbSecurityException(ex.Message);
                throw;
            }
        }

        private void DeleteInternal<T>(IEnumerable<string> ids)
        {
            if (ids == null)
                return;

            int batchCount = 0;
            var batchRequest = CreateBatchDeleteRequest<T>();

            foreach (var id in ids)
            {
                batchCount++;

                var batchItem = new DeleteableItem() { ItemName = id };
                batchRequest.WithItem(batchItem);

                if (batchCount == 25)
                {
                    try
                    {
                        BatchDelete(batchRequest);

                    }
                    catch (Exception ex)
                    {
                        Log.Error("ERROR: ScrappyDbCollection<T>.Save: BatchPut - {0}", ex.Message);
                    }

                    batchRequest = CreateBatchDeleteRequest<T>();
                    batchCount = 0;
                }
            }

            if (batchRequest.Item.Count > 0)
                BatchDelete(batchRequest);
        }

        public BatchDeleteAttributesRequest CreateBatchDeleteRequest<T>()
        {
            var o = Activator.CreateInstance<T>();
            string domainName = EntityHelper.GetDomainName(o);

            return CreateBatchDeleteRequest(domainName);
        }

        private static BatchDeleteAttributesRequest CreateBatchDeleteRequest(string domainName)
        {
            Log.Debug("ScrappyDB.CreateBatchDeleteRequest: Domain: {0}  ", domainName);

            return new BatchDeleteAttributesRequest { DomainName = domainName };
        }

        public void BatchDelete(BatchDeleteAttributesRequest batchRequest)
        {
            Log.Info("ScrappyDB.BatchDelete: {0} items - Domain: {1} ", batchRequest.Item.Count, batchRequest.DomainName);

            try
            {
                var result = Service.BeginBatchDeleteAttributes(batchRequest, r =>
                    {
                        Log.Info("BatchDelete Async Callback");
                        var response = Service.EndBatchDeleteAttributes(r);
                        if (response.IsSetResponseMetadata())
                            Utility.TraceResponse(response.ResponseMetadata, response.ToXML());
                    }, null);


            }
            catch (Exception ex)
            {
                if (ex.Message.Contains("does not have permission"))
                    throw new SdbExceptions.SdbSecurityException(ex.Message);
                throw;
            }
        }
    }
}